package tools

import java.io.File
import java.nio.file.Files

import config.MyFile
import org.apache.commons.io.FileUtils
import play.api.data.Form
import play.api.data.Forms._
import utils.ExecCommand
import scala.jdk.CollectionConverters._

object Primer3 extends MyFile {


  case class primerData(species: String, chr: String, start: String, end: String, SEQUENCE_TARGET: String, PRIMER_PRODUCT_SIZE_RANGE: String,
                        PRIMER_OPT_SIZE: String,
                        PRIMER_MAX_SIZE: String, PRIMER_MIN_SIZE: String, PRIMER_OPT_TM: String, PRIMER_MAX_TM: String,
                        PRIMER_MIN_TM: String, PRIMER_OPT_GC_PERCENT: String, PRIMER_MAX_GC: String, PRIMER_MIN_GC: String,
                        PRIMER_MAX_NS_ACCEPTED: String, PRIMER_MAX_POLY_X: String, PRIMER_INTERNAL_MAX_POLY_X: String,
                        PRIMER_MAX_SELF_ANY: String, PRIMER_MAX_SELF_END: String, PRIMER_PAIR_MAX_COMPL_ANY: String,
                        PRIMER_PAIR_MAX_COMPL_END: String)

  val primerForm = Form(
    mapping(
      "species" -> text,
      "chr" -> text,
      "start" -> text,
      "end" -> text,
      "SEQUENCE_TARGET" -> text,
      "PRIMER_PRODUCT_SIZE_RANGE" -> text,
      "PRIMER_OPT_SIZE" -> text,
      "PRIMER_MAX_SIZE" -> text,
      "PRIMER_MIN_SIZE" -> text,
      "PRIMER_OPT_TM" -> text,
      "PRIMER_MAX_TM" -> text,
      "PRIMER_MIN_TM" -> text,
      "PRIMER_OPT_GC_PERCENT" -> text,
      "PRIMER_MAX_GC" -> text,
      "PRIMER_MIN_GC" -> text,
      "PRIMER_MAX_NS_ACCEPTED" -> text,
      "PRIMER_MAX_POLY_X" -> text,
      "PRIMER_INTERNAL_MAX_POLY_X" -> text,
      "PRIMER_MAX_SELF_ANY" -> text,
      "PRIMER_MAX_SELF_END" -> text,
      "PRIMER_PAIR_MAX_COMPL_ANY" -> text,
      "PRIMER_PAIR_MAX_COMPL_END" -> text
    )(primerData.apply)(primerData.unapply)
  )

  case class primerData2(method: String, queryText: String)

  val primerForm2 = Form(
    mapping(
      "method" -> text,
      "queryText" -> text
    )(primerData2.apply)(primerData2.unapply)
  )


  def runPrimer(fa: String, data: primerData,tmp:String) = {

    var valid = "false"
    var message = ""
    var result = ""

    val exec = new ExecCommand()
    val input =
      s"""SEQUENCE_ID=example
         |SEQUENCE_TEMPLATE=$fa
         |SEQUENCE_TARGET=${data.SEQUENCE_TARGET}
         |PRIMER_TASK=generic
         |PRIMER_PICK_LEFT_PRIMER=1
         |PRIMER_PICK_INTERNAL_OLIGO=1
         |PRIMER_PICK_RIGHT_PRIMER=1
         |PRIMER_OPT_SIZE=${data.PRIMER_OPT_SIZE}
         |PRIMER_MIN_SIZE=${data.PRIMER_MIN_SIZE}
         |PRIMER_MAX_SIZE=${data.PRIMER_MAX_SIZE}
         |PRIMER_OPT_TM=${data.PRIMER_OPT_TM}
         |PRIMER_MAX_TM=${data.PRIMER_MAX_TM}
         |PRIMER_MIN_TM=${data.PRIMER_MIN_TM}
         |PRIMER_OPT_GC_PERCENT=${data.PRIMER_OPT_GC_PERCENT}
         |PRIMER_MAX_GC=${data.PRIMER_MAX_GC}
         |PRIMER_MIN_GC=${data.PRIMER_MIN_GC}
         |PRIMER_MAX_NS_ACCEPTED=${data.PRIMER_MAX_NS_ACCEPTED}
         |PRIMER_MAX_POLY_X=${data.PRIMER_MAX_POLY_X}
         |PRIMER_INTERNAL_MAX_POLY_X=${data.PRIMER_INTERNAL_MAX_POLY_X}
         |PRIMER_MAX_SELF_ANY=${data.PRIMER_MAX_SELF_ANY}
         |PRIMER_MAX_SELF_END=${data.PRIMER_MAX_SELF_END}
         |PRIMER_PAIR_MAX_COMPL_ANY=${data.PRIMER_PAIR_MAX_COMPL_ANY}
         |PRIMER_PAIR_MAX_COMPL_END=${data.PRIMER_PAIR_MAX_COMPL_END}
         |PRIMER_PRODUCT_SIZE_RANGE=${data.PRIMER_PRODUCT_SIZE_RANGE.split(",").mkString(" ")}
         |P3_FILE_FLAG=1
         |SEQUENCE_INTERNAL_EXCLUDED_REGION=${data.SEQUENCE_TARGET}
         |PRIMER_EXPLAIN_FLAG=1
         |=""".stripMargin

    FileUtils.writeLines(s"$tmp/setting.txt".toFile, input.split("\n").map(_.trim).toBuffer.asJava)

    val command = s"primer3_core -format_output -default_version=2 -io_version=4 -strict_tags -output=$tmp/out.txt " +
      s"-error=$tmp/error.txt $tmp/setting.txt"

    exec.exect(Array(command), tmp)

    if (exec.isSuccess) {

      try {
        valid = "true"
        val out =s"$tmp/out.txt".readFileToString
        val start = data.start match{
          case "" => 1
          case _ =>  data.start.toInt
        }
        if (out.contains("NO PRIMERS FOUND")) {
          result = Primer3.getNonePrimerResult(tmp, start)
        } else {
          result = Primer3.getPrimerResult(tmp, start)
        }
      } catch {
        case e: Exception =>
          valid = "false"
          message = e.getMessage
      }
    } else {
      valid = "false"
      message = s"$tmp/error.txt".readFileToString
    }
    tmp.delete
    (valid, message, result)
  }

  def getPrimerResult(tmp: String, start: Int): String = {

    val f = s"$tmp/out.txt".readLines
    val i = f.filter(_.contains("INTERNAL OLIGO EXCLUDED REGIONS")).head
    val i2 = f.filter(_.contains("KEYS (in order of precedence):")).head

    val s = f.slice(f.indexOf(i) + 2, f.indexOf(i2))

    val seqs = s.grouped(3).map { x =>
      val seq = x.head.split(" ")
      val c1 = "<tr><td class='length'>" + (seq.init.last.toInt + start) + "</td> " +
        seq.last.split("").map(y => "<td>" + y + "</td>").mkString + "</tr>"

      val index = x.head.indexOf(seq.last.head) - 1

      val c2 = "<tr>" + x(1).split("").drop(index).map {
        case y if y == "*" => "<td class='blue'>" + y + "</td>"
        case y if y == "x" => "<td class='blue'>" + y + "</td>"
        case y if y == ">" => "<td class='orange'>" + y + "</td>"
        case y if y == "<" => "<td class='orange'>" + y + "</td>"
        case y if y == "^" => "<td class='orange'>" + y + "</td>"
        case y: String => "<td>" + y + "</td>"
      }.mkString + "</tr>"
      val c3 = "<tr><td>" + x.last + "</td></tr>"
      c1 + c2 + c3
    }.mkString

    val head = "OLIGO            start  len      tm     gc%  any_th  3'_th hairpin seq"
    val t1 = f.indexOf(head) + 1
    val t2 = f.filter(_.contains("SEQUENCE SIZE")).head
    val t = f.slice(t1, f.indexOf(t2))

    val r1 = t.map { x =>
      val td = x.split("\\s+")
      "<tr class='tableText'><td>" + td.take(2).mkString("\t") + "</td><td>" + (td(2).toInt + start) + "</td>" +
        td.drop(3).map { y =>
          "<td>" + y + "</td>"
        }.mkString + "</tr>"
    }.mkString

    val a1 = f.indexOf("ADDITIONAL OLIGOS") + 3
    val a2 = f.indexOf("Statistics")

    val rw = f.slice(a1, a2).grouped(5).map(_.take(3)).toArray
    val r2 = rw.map { x =>
      val array = x.map(_.split("\\s+"))
      val a = array.head.tail +: array.tail

      a.map { td =>
        "<tr><td>" + td.head + "</td><td>" + td.slice(2, 4).mkString("\t") + "</td><td>" + (td(3).toInt + start) + "</td>" +
          td.drop(4).map { y =>
            "<td>" + y + "</td>"
          }.mkString + "</tr>"
      }.mkString
    }.mkString

    val seqSizeIndex = f.indexOf(t2)
    val seqSize = "<p>" + f(seqSizeIndex) + "</p><p>" + f(seqSizeIndex + 1) + "</p><br>"

    val productSize = "<p>" + f(seqSizeIndex + 3) + "</p><p>" + f(seqSizeIndex + 4) + "</p><p>" + f(seqSizeIndex + 5) + "</p>"

    val statIndex = f.indexOf("Statistics")

    val stat = f.slice(statIndex + 4, statIndex + 7).map { x =>
      "<tr>" + x.split("\\s+").map(y => "<td>" + y + "</td>").mkString + "</tr>"
    }.mkString

    val pairIndex = f.indexOf("Pair Stats:")
    val pairStats = f.slice(pairIndex + 1, pairIndex + 3).map(x => "<p>" + x + "</p>")


    val html =
      s"""
         |        <hr>
         |        <h3>Primer design results:</h3>
         |        <div class="table-responsive panel-collapse in " >
         |            <table class="table" style="word-wrap: break-word">
         |                <thead>
         |                    <tr class="tableHead">
         |                        <th data-field="oligos">Oligos</th>
         |                        <th data-field="start">Start position</th>
         |                        <th data-field="length">Length</th>
         |                        <th data-field="tm">Tm</th>
         |                        <th data-field="gc">GC percent</th>
         |                        <th data-field="any">Self any</th>
         |                        <th data-field="end">Self end</th>
         |                        <th data-field="end">Hairpin</th>
         |                        <th data-field="seq">Sequence</th>
         |                    </tr>
         |                </thead>
         |                <tbody>
         |                ${r1.mkString}
         |                </tbody>
         |            </table>
         |        </div>
         |        $seqSize
         |
         |        <div>
         |
         |            <h3>Primer design results in the sequence:</h3>
         |
         |            $productSize
         |            <table>
         |                <tbody id="seq">
         |                ${seqs.mkString}
         |
         |                </tbody>
         |            </table>
         |        </div>
         |
         |
         |        <h3>Note:</h3>
         |        <table>
         |            <tbody>
         |                <tr>
         |                    <td class="blue" style="width: 80px;">xxxxxxx</td>
         |                    <td>excluded region for internal oligo</td>
         |                </tr>
         |                <tr>
         |                    <td class="orange">>>>>>></td>
         |                    <td>left primer</td>
         |                </tr>
         |                <tr>
         |                    <td class="orange"><<<<<<</td>
         |                    <td>right primer</td>
         |                </tr>
         |                <tr>
         |                    <td class="orange">^^^^^^^^</td>
         |                    <td>right primer</td>
         |                </tr>
         |
         |            </tbody>
         |        </table>
         |
         |
         |        <h3>ADDITIONAL OLIGOS:</h3>
         |        <div class="table-responsive panel-collapse in " >
         |            <table class="table table-hover table-striped" style="word-wrap: break-word">
         |                <thead>
         |                    <tr>
         |                    <th></th>
         |                        <th>Oligos</th>
         |                        <th>Start position</th>
         |                        <th>Length</th>
         |                        <th>Tm</th>
         |                        <th>GC percent</th>
         |                        <th>Self any</th>
         |                        <th>Self end</th>
         |                        <th>Hairpin</th>
         |                        <th>Sequence</th>
         |                    </tr>
         |                </thead>
         |                <tbody id="additionalTable">
         |                ${r2.mkString}
         |
         |                </tbody>
         |            </table>
         |        </div>
         |
         |        <h3>Statistics:</h3>
         |        <div class="table-responsive panel-collapse in " >
         |            <table class="table table-hover table-striped" style="word-wrap: break-word">
         |                <thead>
         |                    <tr>
         |                        <th>Direction</th>
         |                        <th>Considered</th>
         |                        <th>Too many Ns</th>
         |                        <th>In target</th>
         |                        <th>In excel reg</th>
         |                        <th>Not ok reg</th>
         |                        <th>Bad GC%</th>
         |                        <th>No GC clamp</th>
         |                        <th>TM too low</th>
         |                        <th>TM too high</th>
         |                        <th>High any_th compl</th>
         |                        <th>High 3'_th compl</th>
         |                        <th>Highhair-pin</th>
         |                        <th>Poly x</th>
         |                        <th>High end stab</th>
         |                        <th>Ok</th>
         |                    </tr>
         |                </thead>
         |                <tbody id="additionalTable">
         |                $stat
         |
         |
         |                </tbody>
         |            </table>
         |        </div>
         |
         |
         |        <h3>Pair Stats:</h3>
         |       ${pairStats.mkString}
    """.stripMargin

    html

  }

  def getNonePrimerResult(tmp: String, start: Int): String = {
    val f = s"$tmp/out.txt".readLines

    val i = f.filter(_.contains("INTERNAL OLIGO EXCLUDED REGIONS")).head
    val i2 = f.filter(_.contains("KEYS (in order of precedence):")).head

    val s = f.slice(f.indexOf(i) + 2, f.indexOf(i2))

    val seqs = s.grouped(3).map { x =>
      val seq = x.head.split(" ")
      val c1 = "<tr><td class='length'>" + (seq.init.last.toInt + start) + "</td> " +
        seq.last.split("").map(y => "<td>" + y + "</td>").mkString + "</tr>"

      val index = x.head.indexOf(seq.last.head) - 1

      val c2 = "<tr>" + x(1).split("").drop(index).map {
        case y if y == "*" => "<td class='blue'>" + y + "</td>"
        case y if y == "x" => "<td class='blue'>" + y + "</td>"
        case y if y == ">" => "<td class='orange'>" + y + "</td>"
        case y if y == "<" => "<td class='orange'>" + y + "</td>"
        case y if y == "^" => "<td class='orange'>" + y + "</td>"
        case y: String => "<td>" + y + "</td>"
      }.mkString + "</tr>"
      val c3 = "<tr><td>" + x.last + "</td></tr>"
      c1 + c2 + c3
    }.mkString

    val t2 = f.filter(_.contains("SEQUENCE SIZE")).head

    val seqSizeIndex = f.indexOf(t2)
    val seqSize = "<p>" + f(seqSizeIndex) + "</p><p>" + f(seqSizeIndex + 1) + "</p><br>"

    val productSize = "<p>" + f(seqSizeIndex + 3) + "</p><p>" + f(seqSizeIndex + 4) + "</p><p>" + f(seqSizeIndex + 5) + "</p>"

    val statIndex = f.indexOf("Statistics")

    val stat = f.slice(statIndex + 4, statIndex + 7).map { x =>
      "<tr>" + x.split("\\s+").map(y => "<td>" + y + "</td>").mkString + "</tr>"
    }.mkString

    val pairIndex = f.indexOf("Pair Stats:")
    val pairStats = f.slice(pairIndex + 1, pairIndex + 3).map(x => "<p>" + x + "</p>")


    val html =
      s"""
         |        <hr>
         |        <h3>NO PRIMERS FOUND</h3>
         |        </div>
         |        $seqSize
         |
         |        <div>
         |            $productSize
         |            <table>
         |                <tbody id="seq">
         |                ${seqs.mkString}
         |
         |                </tbody>
         |            </table>
         |        </div>
         |
         |
         |        <h3>Note:</h3>
         |        <table>
         |            <tbody>
         |                <tr>
         |                    <td class="blue" style="width: 80px;">xxxxxxx</td>
         |                    <td>excluded region for internal oligo</td>
         |                </tr>
         |            </tbody>
         |        </table>
         |
         |<br/>
         |
         |        <h3>Statistics:</h3>
         |        <div class="table-responsive panel-collapse in " >
         |            <table class="table table-hover table-striped" style="word-wrap: break-word">
         |                <thead>
         |                    <tr>
         |                        <th>Direction</th>
         |                        <th>Considered</th>
         |                        <th>Too many Ns</th>
         |                        <th>In target</th>
         |                        <th>In excel reg</th>
         |                        <th>Not ok reg</th>
         |                        <th>Bad GC%</th>
         |                        <th>No GC clamp</th>
         |                        <th>TM too low</th>
         |                        <th>TM too high</th>
         |                        <th>High any_th compl</th>
         |                        <th>High 3'_th compl</th>
         |                        <th>Highhair-pin</th>
         |                        <th>Poly x</th>
         |                        <th>High end stab</th>
         |                        <th>Ok</th>
         |                    </tr>
         |                </thead>
         |                <tbody id="additionalTable">
         |                $stat
         |
         |
         |                </tbody>
         |            </table>
         |        </div>
         |
         |
         |        <h3>Pair Stats:</h3>
         |       ${pairStats.mkString}
    """.stripMargin
    html
  }


}
